#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Git Hooks 动态配置系统 - Hook 管理器
负责管理各个 Git Hook 阶段的执行
"""

import os
import sys
import logging
import subprocess
from typing import Dict, List, Any, Optional
from pathlib import Path
from dataclasses import dataclass
from enum import Enum

from .config_parser import ConfigParser, GlobalConfig, HookConfig, TaskConfig
from .task_executor import TaskExecutor, TaskResult, TaskStatus


class HookType(Enum):
    """Hook 类型枚举"""
    PRE_COMMIT = "pre-commit"
    PREPARE_COMMIT_MSG = "prepare-commit-msg"
    COMMIT_MSG = "commit-msg"
    POST_COMMIT = "post-commit"
    PRE_PUSH = "pre-push"
    POST_MERGE = "post-merge"
    PRE_RECEIVE = "pre-receive"
    UPDATE = "update"
    POST_RECEIVE = "post-receive"


@dataclass
class HookContext:
    """Hook 执行上下文"""
    hook_type: HookType
    staged_files: List[str] = None
    commit_msg: str = ""
    current_branch: str = ""
    commit_count: int = 0
    git_dir: str = ""
    working_dir: str = ""
    metadata: Dict[str, Any] = None


class HookManager:
    """Hook 管理器"""
    
    def __init__(self, config_dir: str = "config", plugins_dir: str = "plugins"):
        self.config_dir = Path(config_dir)
        self.plugins_dir = Path(plugins_dir)
        self.logger = logging.getLogger(__name__)
        
        # 初始化组件
        self.config_parser = ConfigParser(str(self.config_dir))
        self.task_executor = TaskExecutor(str(self.config_dir), str(self.plugins_dir))
        
        # 加载配置
        self.global_config = self.config_parser.get_global_config()
        if not self.global_config:
            self.logger.error("无法加载全局配置")
            sys.exit(1)
        
        # 设置日志
        self._setup_logging()
    
    def _setup_logging(self):
        """设置日志"""
        log_level = getattr(logging, self.global_config.log_level.value.upper())
        
        # 创建日志格式
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )
        
        # 文件处理器
        if self.global_config.log_file:
            # 确保日志文件的父目录存在
            log_file_path = Path(self.global_config.log_file)
            log_file_path.parent.mkdir(parents=True, exist_ok=True)
            
            file_handler = logging.FileHandler(self.global_config.log_file)
            file_handler.setFormatter(formatter)
            file_handler.setLevel(log_level)
            self.logger.addHandler(file_handler)
        
        # 控制台处理器
        console_handler = logging.StreamHandler()
        console_handler.setFormatter(formatter)
        console_handler.setLevel(log_level)
        self.logger.addHandler(console_handler)
        
        self.logger.setLevel(log_level)
    
    def execute_hook(self, hook_type: HookType, context: HookContext = None) -> bool:
        """执行指定的 Hook"""
        if not self.global_config.enabled:
            self.logger.info("系统已禁用，跳过 Hook 执行")
            return True
        
        self.logger.info(f"开始执行 Hook: {hook_type.value}")
        
        try:
            # 获取 Hook 配置
            hook_config = self.config_parser.get_hook_config(hook_type.value)
            if not hook_config or not hook_config.enabled:
                self.logger.info(f"Hook {hook_type.value} 未启用，跳过执行")
                return True
            
            # 准备上下文
            if context is None:
                context = self._create_default_context(hook_type)
            
            # 执行任务
            success = self._execute_hook_tasks(hook_config, context)
            
            if success:
                self.logger.info(f"Hook {hook_type.value} 执行成功")
            else:
                self.logger.error(f"Hook {hook_type.value} 执行失败")
            
            return success
            
        except Exception as e:
            self.logger.error(f"Hook 执行异常: {hook_type.value}, 错误: {e}")
            return False
    
    def _create_default_context(self, hook_type: HookType) -> HookContext:
        """创建默认上下文"""
        context = HookContext(hook_type=hook_type)
        
        try:
            # 获取 Git 信息
            context.git_dir = self._get_git_dir()
            context.working_dir = os.getcwd()
            
            # 获取当前分支
            context.current_branch = self._get_current_branch()
            
            # 获取暂存文件
            if hook_type in [HookType.PRE_COMMIT, HookType.PREPARE_COMMIT_MSG]:
                context.staged_files = self._get_staged_files()
            
            # 获取提交信息
            if hook_type in [HookType.COMMIT_MSG, HookType.PREPARE_COMMIT_MSG]:
                context.commit_msg = self._get_commit_message()
            
            # 获取提交数量
            context.commit_count = self._get_commit_count()
            
        except Exception as e:
            self.logger.warning(f"获取上下文信息失败: {e}")
        
        return context
    
    def _execute_hook_tasks(self, hook_config: HookConfig, context: HookContext) -> bool:
        """执行 Hook 任务"""
        if not hook_config.tasks:
            self.logger.info("没有配置任务，跳过执行")
            return True
        
        # 过滤启用的任务
        enabled_tasks = [task for task in hook_config.tasks if task.enabled]
        if not enabled_tasks:
            self.logger.info("没有启用的任务，跳过执行")
            return True
        
        self.logger.info(f"执行 {len(enabled_tasks)} 个任务")
        
        # 检查任务是否支持并行执行
        parallel_tasks = [task for task in enabled_tasks if task.parallel]
        sequential_tasks = [task for task in enabled_tasks if not task.parallel]
        
        all_results = []
        
        # 执行并行任务
        if parallel_tasks:
            self.logger.info(f"并行执行 {len(parallel_tasks)} 个任务")
            parallel_results = self.task_executor.execute_tasks_parallel(parallel_tasks, self._context_to_dict(context))
            all_results.extend(parallel_results)
        
        # 执行顺序任务
        if sequential_tasks:
            self.logger.info(f"顺序执行 {len(sequential_tasks)} 个任务")
            sequential_results = self.task_executor.execute_tasks_sequential(sequential_tasks, self._context_to_dict(context))
            all_results.extend(sequential_results)
        
        # 分析结果
        return self._analyze_results(all_results)
    
    def _analyze_results(self, results: List[TaskResult]) -> bool:
        """分析任务执行结果"""
        if not results:
            return True
        
        success_count = 0
        failed_count = 0
        skipped_count = 0
        
        for result in results:
            if result.status == TaskStatus.SUCCESS:
                success_count += 1
                self.logger.info(f"任务成功: {result.task_name}")
            elif result.status == TaskStatus.FAILED:
                failed_count += 1
                self.logger.error(f"任务失败: {result.task_name}")
                if result.error:
                    self.logger.error(f"错误信息: {result.error}")
            elif result.status == TaskStatus.SKIPPED:
                skipped_count += 1
                self.logger.info(f"任务跳过: {result.task_name}")
            elif result.status == TaskStatus.TIMEOUT:
                failed_count += 1
                self.logger.error(f"任务超时: {result.task_name}")
        
        self.logger.info(f"任务执行统计: 成功 {success_count}, 失败 {failed_count}, 跳过 {skipped_count}")
        
        # 根据全局配置决定是否阻止提交
        if failed_count > 0 and self.global_config.fail_fast:
            self.logger.error("存在失败的任务，阻止提交")
            return False
        
        return True
    
    def _context_to_dict(self, context: HookContext) -> Dict[str, Any]:
        """将上下文转换为字典"""
        return {
            'staged_files': context.staged_files or [],
            'commit_msg': context.commit_msg,
            'current_branch': context.current_branch,
            'commit_count': context.commit_count,
            'git_dir': context.git_dir,
            'working_dir': context.working_dir,
            'metadata': context.metadata or {}
        }
    
    def _get_git_dir(self) -> str:
        """获取 Git 目录"""
        try:
            result = subprocess.run(['git', 'rev-parse', '--git-dir'], 
                                  capture_output=True, text=True, check=True)
            return result.stdout.strip()
        except subprocess.CalledProcessError:
            return ""
    
    def _get_current_branch(self) -> str:
        """获取当前分支"""
        try:
            result = subprocess.run(['git', 'branch', '--show-current'], 
                                  capture_output=True, text=True, check=True)
            return result.stdout.strip()
        except subprocess.CalledProcessError:
            return ""
    
    def _get_staged_files(self) -> List[str]:
        """获取暂存文件列表"""
        try:
            result = subprocess.run(['git', 'diff', '--cached', '--name-only'], 
                                  capture_output=True, text=True, check=True)
            return [line.strip() for line in result.stdout.split('\n') if line.strip()]
        except subprocess.CalledProcessError:
            return []
    
    def _get_commit_message(self) -> str:
        """获取提交信息"""
        try:
            # 尝试从文件读取
            commit_msg_file = os.environ.get('GIT_EDITMSG', '')
            if commit_msg_file and os.path.exists(commit_msg_file):
                with open(commit_msg_file, 'r', encoding='utf-8') as f:
                    return f.read().strip()
            
            # 尝试从环境变量读取
            return os.environ.get('GIT_COMMIT_MSG', '')
        except Exception:
            return ""
    
    def _get_commit_count(self) -> int:
        """获取提交数量"""
        try:
            result = subprocess.run(['git', 'rev-list', '--count', 'HEAD'], 
                                  capture_output=True, text=True, check=True)
            return int(result.stdout.strip())
        except (subprocess.CalledProcessError, ValueError):
            return 0
    
    def install_hooks(self, git_dir: str = None) -> bool:
        """安装 Git Hooks"""
        if not git_dir:
            git_dir = self._get_git_dir()
        
        if not git_dir:
            self.logger.error("无法找到 Git 目录")
            return False
        
        hooks_dir = Path(git_dir) / "hooks"
        if not hooks_dir.exists():
            self.logger.error(f"Hooks 目录不存在: {hooks_dir}")
            return False
        
        # 创建 Hook 脚本
        hook_scripts = {
            'pre-commit': self._create_pre_commit_script(),
            'commit-msg': self._create_commit_msg_script(),
            'pre-push': self._create_pre_push_script(),
            'prepare-commit-msg': self._create_prepare_commit_msg_script(),
            'post-commit': self._create_post_commit_script(),
            'post-merge': self._create_post_merge_script()
        }
        
        success_count = 0
        for hook_name, script_content in hook_scripts.items():
            hook_file = hooks_dir / hook_name
            try:
                with open(hook_file, 'w', encoding='utf-8') as f:
                    f.write(script_content)
                
                # 设置执行权限
                os.chmod(hook_file, 0o755)
                self.logger.info(f"安装 Hook: {hook_name}")
                success_count += 1
                
            except Exception as e:
                self.logger.error(f"安装 Hook 失败: {hook_name}, 错误: {e}")
        
        self.logger.info(f"成功安装 {success_count} 个 Hook")
        return success_count > 0
    
    def _create_pre_commit_script(self) -> str:
        """创建 pre-commit Hook 脚本"""
        # 检测 Python 命令
        python_cmd = self._get_python_command()
        return f"""#!/bin/bash
# Git Hooks 动态配置系统 - pre-commit Hook

cd "{os.getcwd()}"
{python_cmd} -c "
import sys
sys.path.insert(0, '{os.getcwd()}')
from core.hook_manager import HookManager, HookType, HookContext

manager = HookManager()
context = HookContext(hook_type=HookType.PRE_COMMIT)
success = manager.execute_hook(HookType.PRE_COMMIT, context)
sys.exit(0 if success else 1)
"
"""
    
    def _create_commit_msg_script(self) -> str:
        """创建 commit-msg Hook 脚本"""
        python_cmd = self._get_python_command()
        return f"""#!/bin/bash
# Git Hooks 动态配置系统 - commit-msg Hook

cd "{os.getcwd()}"
{python_cmd} -c "
import sys
sys.path.insert(0, '{os.getcwd()}')
from core.hook_manager import HookManager, HookType, HookContext

manager = HookManager()
context = HookContext(hook_type=HookType.COMMIT_MSG)

if len(sys.argv) > 1:
    with open(sys.argv[1], 'r', encoding='utf-8') as f:
        context.commit_msg = f.read().strip()
else:
    context.commit_msg = ''

success = manager.execute_hook(HookType.COMMIT_MSG, context)
sys.exit(0 if success else 1)
" "$@"
"""
    
    def _create_pre_push_script(self) -> str:
        """创建 pre-push Hook 脚本"""
        python_cmd = self._get_python_command()
        return f"""#!/bin/bash
# Git Hooks 动态配置系统 - pre-push Hook

cd "{os.getcwd()}"
{python_cmd} -c "
import sys
sys.path.insert(0, '{os.getcwd()}')
from core.hook_manager import HookManager, HookType, HookContext

manager = HookManager()
context = HookContext(hook_type=HookType.PRE_PUSH)
success = manager.execute_hook(HookType.PRE_PUSH, context)
sys.exit(0 if success else 1)
"
"""
    
    def _create_prepare_commit_msg_script(self) -> str:
        """创建 prepare-commit-msg Hook 脚本"""
        python_cmd = self._get_python_command()
        return f"""#!/bin/bash
# Git Hooks 动态配置系统 - prepare-commit-msg Hook

cd "{os.getcwd()}"
{python_cmd} -c "
import sys
sys.path.insert(0, '{os.getcwd()}')
from core.hook_manager import HookManager, HookType, HookContext

manager = HookManager()
context = HookContext(hook_type=HookType.PREPARE_COMMIT_MSG)
success = manager.execute_hook(HookType.PREPARE_COMMIT_MSG, context)
sys.exit(0 if success else 1)
"
"""
    
    def _create_post_commit_script(self) -> str:
        """创建 post-commit Hook 脚本"""
        python_cmd = self._get_python_command()
        return f"""#!/bin/bash
# Git Hooks 动态配置系统 - post-commit Hook

cd "{os.getcwd()}"
{python_cmd} -c "
import sys
sys.path.insert(0, '{os.getcwd()}')
from core.hook_manager import HookManager, HookType, HookContext

manager = HookManager()
context = HookContext(hook_type=HookType.POST_COMMIT)
success = manager.execute_hook(HookType.POST_COMMIT, context)
sys.exit(0 if success else 1)
"
"""
    
    def _create_post_merge_script(self) -> str:
        """创建 post-merge Hook 脚本"""
        python_cmd = self._get_python_command()
        return f"""#!/bin/bash
# Git Hooks 动态配置系统 - post-merge Hook

cd "{os.getcwd()}"
{python_cmd} -c "
import sys
sys.path.insert(0, '{os.getcwd()}')
from core.hook_manager import HookManager, HookType, HookContext

manager = HookManager()
context = HookContext(hook_type=HookType.POST_MERGE)
success = manager.execute_hook(HookType.POST_MERGE, context)
sys.exit(0 if success else 1)
"
"""
    
    def _get_python_command(self) -> str:
        """获取正确的 Python 命令"""
        import platform
        
        # 在 Windows 上，通常使用 python 而不是 python3
        if platform.system() == "Windows":
            # 首先尝试使用虚拟环境中的 Python
            venv_python = Path(os.getcwd()) / ".venv" / "Scripts" / "python.exe"
            if venv_python.exists():
                # 在 Windows 上，将路径转换为正斜杠格式，以便在 bash 脚本中使用
                return str(venv_python).replace("\\", "/")
            return "python"
        else:
            # 在 Linux/Mac 上，优先使用 python3
            try:
                subprocess.run(['python3', '--version'], capture_output=True, check=True)
                return "python3"
            except (subprocess.CalledProcessError, FileNotFoundError):
                return "python"


if __name__ == "__main__":
    # 测试 Hook 管理器
    manager = HookManager()
    
    # 测试安装 Hooks
    print("安装 Git Hooks...")
    success = manager.install_hooks()
    print(f"安装结果: {'成功' if success else '失败'}")
    
    # 测试执行 Hook
    print("测试执行 pre-commit Hook...")
    # HookType 和 HookContext 已经在当前模块中定义
    
    context = HookContext(hook_type=HookType.PRE_COMMIT)
    success = manager.execute_hook(HookType.PRE_COMMIT, context)
    print(f"执行结果: {'成功' if success else '失败'}")
